/*
 * Copyright 2011 Philippe Blanc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package me.l1k3.fx.client.engine.easing;

//http://jsdo.it/gct256/Easing
//http://svn.webkit.org/repository/webkit/trunk/WebCore/platform/graphics/UnitBezier.h
public final class CubicBezier implements Easing {
    private double x1;
    private double y1;
    private double x2;
    private double y2;
    
    private double cx;
	private double bx;
	private double ax;
	private double cy;
	private double by;
	private double ay;
	
	public CubicBezier(double x1, double y1, double x2, double y2) {
	    this.x1 = x1;
	    this.y1 = y1;
	    this.x2 = x2;
        this.y2 = y2;
	    
	    cx = 3 * x1;
	    bx = 3 * (x2 - x1) - cx;
	    ax = 1 - cx - bx;
	    cy = 3 * y1;
	    by = 3 * (y2 - y1) - cy;
	    ay = 1 - cy - by;
	}
	
	@Override
	public final double interpolate(double progress) {
		double t0 = 0;
		double t1 = 1;
		double t2 = progress;
		
		for (int i=0;i<8;i++) {
		    double x2 = (((ax * t2 + bx) * t2 + cx) * t2) - progress;
			
			if (-0.000001 < x2 && x2 < 0.000001) {
				return ((ay*t2+by)*t2+cy)*t2;
			}
			
			double d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
			
			if (-0.000001 < d2 && d2 < 0.000001) {
				break;
			}
			
			t2 = t2 - x2 / d2;
		}
		
		t2 = progress;
		
		while (t0 < t1) {
		    double x2 = ((ay * t2 + by) * t2 + cy) * t2;
			double i = x2 - progress;
			
			if (-0.000001 < i && i < 0.000001) {
				return ((ay*t2+by)*t2+cy)*t2;
			}
			
			if (progress > x2) {
				t0 = t2;
			}
			else {
				t1 = t2;
			}
			
			t2 = (t1 - t0) * 0.5 + t0;
		}
		
		return ((ay*t2+by)*t2+cy)*t2;
	}
	
    @Override
    public final String getCSS3Easing() throws Easing.EasingNotSupportedException {
        return "cubic-bezier("+x1+", "+y1+", "+x2+", "+y2+")";
    }
}
